
#include <QVBoxLayout>
#include "wpcommentlistitem.h"
#include "wpcommentlist.h"
#include "WPData.h"
#include "WPNetworkEngine.h"
#include "wpscrollarea.h"
#include "PopupMenu.h"
#include "themedatastorage.h"

#ifdef Q_WS_MAEMO_5
static const int KTitleDefaultHeight = 45;
#else
static const int KTitleDefaultHeight = 32;
#endif

WPCommentList::WPCommentList(
        QListIterator<WPComment> aData,
        WPDataGravatars *aGravatars,
        WPScrollArea& aScrollArea, 
        ThemeData& aThemeData, 
        bool aShowFullComment
    ) : 
        MDragWidget(NULL), 
        iGravatars(aGravatars),
        iScrollArea(aScrollArea),
        iCurrentItem(NULL),
        iUnapproved(0), iApproved(0)
{
    iLayout = new QVBoxLayout;
    iLayout->setSpacing(0);
    iLayout->setContentsMargins(0,0,0,0);

    iActionApprove = new QAction(aThemeData.GetContextIcon(ThemeData::EApprove),tr("    Approve"), this);
    iActionUnapprove = new QAction(aThemeData.GetContextIcon(ThemeData::EApprove),tr("    Unapprove"), this);
    iActionView = new QAction(aThemeData.GetContextIcon(ThemeData::EView),tr("    View"), this);
    iActionHide = new QAction(aThemeData.GetContextIcon(ThemeData::EView),tr("    Minimize"), this);
    iActionDelete = new QAction(aThemeData.GetContextIcon(ThemeData::ERemove),tr("    Delete"), this);
    iActionSpam = new QAction(aThemeData.GetContextIcon(ThemeData::ESpam),tr("    Spam"), this);
    iPopupMenu = NULL;

    connect(iActionApprove,   SIGNAL(triggered()), this, SLOT(Approve()));
    connect(iActionUnapprove, SIGNAL(triggered()), this, SLOT(Unapprove()));
    connect(iActionView,      SIGNAL(triggered()), this, SLOT(View()));
    connect(iActionHide,      SIGNAL(triggered()), this, SLOT(View()));
    connect(iActionDelete,    SIGNAL(triggered()), this, SLOT(Delete()));
    connect(iActionSpam,      SIGNAL(triggered()), this, SLOT(Spam()));

    connect(
        iGravatars, SIGNAL(GravatarUpdated (const QString &, const QPixmap &)),
        this,         SLOT(GravatarUpdated (const QString &, const QPixmap &))
    );    

/* XXX:    
   connect(
        iBlogs, SIGNAL(CommentAdded(WPComment)), 
        this,     SLOT(CommentAdded(WPComment))
    );
    connect(
        iBlogs, SIGNAL(CommentChanged(WPComment)), 
        this,     SLOT(CommentChanged(WPComment))
    );
    connect(
        iBlogs, SIGNAL(CommentRemoved(WPComment)), 
        this,     SLOT(CommentRemoved(WPComment))
    );
*/

    iUnapprovedLabel = new QLabel(this);
    iUnapprovedLabel->setObjectName(QString::fromLatin1("unapprovedLabel"));
    iUnapprovedLabel->setFixedHeight(KTitleDefaultHeight);

    // All other comments
    iApprovedLabel = new QLabel(this);
    iApprovedLabel->setObjectName(QString::fromLatin1("approvedLabel"));
    iApprovedLabel->setFixedHeight(KTitleDefaultHeight);

    // add initial comments, inserting labels as needed
    while (aData.hasNext()) {
        WPComment comment = aData.next();

        // sorted insert, with gravatar
        AddComment(comment, aShowFullComment);
    }

    // update label text
    SetLabelText();

    // Sets a minimum height for this container. We can't remove items from this,
    // so we can determine the minimum size pretty accurately: item height plus
    // margin per item.
    int height = 0;
    if (iApproved > 0)
        height += KTitleDefaultHeight; // Height + margin
    if (iUnapproved > 0)
        height += KTitleDefaultHeight; // Height + margin

    // Height + margin
    height += (iApproved + iUnapproved) * (WPCommentListItem::ItemMinimumHeight); 
    setMinimumHeight(height);

    iLayout->addStretch();
    setLayout(iLayout);
}

WPCommentList::~WPCommentList()
{

}

void WPCommentList::AddComment(WPComment aComment, bool aShowFullComment)
{
    WPCommentListItem* item = new WPCommentListItem(*this, aComment, aShowFullComment);
    // Minimum size for the box -- this should some avoid unnecessary redraws
    item->setMinimumHeight(WPCommentListItem::ItemMinimumHeight);
    connect(item, SIGNAL(CommentItemSelected(WPCommentListItem*)), this, SLOT(ShowContextMenu(WPCommentListItem*)));
    
    // get gravatar for item
    item->SetAvatarImage(iGravatars->GetGravatar(aComment));

    // update counters/labels
    if (aComment->IsUnapproved())
        IncrementUnapproved();
    else
        IncrementApproved();

    // insert by increasing age
    int index;

    for (index = 0; index < iItems.size(); index++) {
        if (iItems[index] == NULL) {
            if (aComment->IsUnapproved() && index > 0)
                // unapproved comment at end of unapproved list
                break;

            else
                // placeholder for label, skip
                continue;
        }

        WPComment list_comment = iItems[index]->GetComment();
        
        // unapproved go first
        if (aComment->IsUnapproved() && !list_comment->IsUnapproved())
            // XXX: never run, because of NULL index above
            break;
        
        if (!aComment->IsUnapproved() && list_comment->IsUnapproved())
            // do not mix approved in with unapproved comments
            continue;

        // find first older comment
        if (list_comment->GetLocalTime() < aComment->GetLocalTime())
            break;
    }
    
    // insert before older comment
    iItems.insert(index, item);
    iLayout->insertWidget(index, item);
}

void WPCommentList::RemoveComment (WPComment aComment)
{
    int index;
    WPCommentListItem* item = NULL;

    // find it
    for (index = 0; index < iItems.size(); index++) {
        item = iItems[index];

        if (item && item->GetComment() == aComment)
            // found
            break;

        // sentinel
        item = NULL;
    }

    if (!item)
        // not found
        return;

    // can't use WPComment->IsUnapproved(), because it might have changed
    bool isUnapproved = index <= iUnapproved;

    // drop from list
    iLayout->removeWidget(item);
    iItems.removeAt(index);

    // reset current item
    if (iCurrentItem == item)
        iCurrentItem = NULL;

    item->deleteLater();

    // update count
    if (isUnapproved)
        DecrementUnapproved();
    else
        DecrementApproved();
}

void WPCommentList::GravatarUpdated (const QString &email, const QPixmap &pixmap)
{
    // look for ALL items with matching email and set their pixmap
    for (int i = 0; i < iItems.count(); i++) {
        WPCommentListItem *item = iItems[i];

        // check for placeholders
        if (!item)
            continue;

        // normalize for comparison
        QString normalizedEmail = WPDataGravatars::NormalizeEmail(item->GetComment()->iAuthorEmail);

        if (normalizedEmail == email)
            // update item's pixmap
            item->SetAvatarImage(pixmap);
            
        // set avatar for each matching item
        continue;
    }
}

void WPCommentList::ShowContextMenu(WPCommentListItem* aItem)
{
    if (MDragWidgetIsDragging() || iPopupMenu)
        return;

    iPopupMenu = new PopupMenu(this);
    connect(iPopupMenu, SIGNAL(aboutToHide()), this, SLOT(MenuHiding()));
    iScrollArea.DisableScrollAtWidget(iPopupMenu);
    iCurrentItem = aItem;
    
    if (aItem->GetComment()->IsUnapproved())
    {
        iPopupMenu->addAction(iActionApprove);
        iPopupMenu->setActiveAction(iActionApprove);
    }
    else
    {
        iPopupMenu->addAction(iActionUnapprove);
        iPopupMenu->setActiveAction(iActionUnapprove);
    }
    iPopupMenu->setFocus();
    if (aItem->ShowFullComment())
        iPopupMenu->addAction(iActionHide);
    else
        iPopupMenu->addAction(iActionView);

    iPopupMenu->addAction(iActionDelete);
    if (aItem->GetComment()->iStatus != "spam")
        iPopupMenu->addAction(iActionSpam);

    QPoint itemPoint = aItem->TopRight();
    iPopupMenu->popup(getMenuLocation(itemPoint));
}

bool WPCommentList::MDragWidgetIsDragging() const
{
    return iScrollArea.IsDragging();
}

QSize WPCommentList::VisibleSize() const
{
    return iScrollArea.GetVisibleContentSize();
}

void WPCommentList::Approve()
{
    iCurrentItem->GetComment()->UpdateStatus("approve");
}

void WPCommentList::Unapprove()
{
    iCurrentItem->GetComment()->UpdateStatus("hold");
}

void WPCommentList::View()
{
    iCurrentItem->ChangeViewMode();
}

void WPCommentList::Delete()
{
    iCurrentItem->GetComment()->Delete();
}

void WPCommentList::Spam()
{
    iCurrentItem->GetComment()->UpdateStatus("spam");
}

void WPCommentList::setFocus()
{
    if (iItems.count() > 1)
    {
        // TODO: Fix this as the iItems array might be in wrong order.
        // XXX: wrong order for /what/?
        iItems[1]->setFocus(); 
    }
    else
    {
        QFrame::setFocus();
    }
}

void WPCommentList::MenuHiding()
{
    iScrollArea.RemoveSelfScrollingWidget(iPopupMenu);
    iPopupMenu->deleteLater();
    iPopupMenu = NULL;
}

void WPCommentList::resizeEvent(QResizeEvent* event)
{
    Q_UNUSED(event);

    if (iPopupMenu && iPopupMenu->isVisible())
        iPopupMenu->move(getMenuLocation(iCurrentItem->TopRight()));
}

QPoint WPCommentList::getMenuLocation(QPoint itemPoint)
{
    itemPoint.setY(itemPoint.y() - 5);
    QPoint globalItemPoint = mapToGlobal(itemPoint);
    QSize size = iPopupMenu->sizeHint();

    // Set popup a couple of pixels away from the screen right edge
    itemPoint.setX(itemPoint.x() - size.width() + 20);
    // Make sure the popup won't be placed over the tab bar
    if (globalItemPoint.y() < iScrollArea.TopRightInGlobal().y())
    {
        itemPoint.setY(itemPoint.y() - globalItemPoint.y() + iScrollArea.TopRightInGlobal().y());
    }

    return mapToGlobal(itemPoint);
}

void WPCommentList::CommentChanged(WPComment aComment)
{
    // mmm
    RemoveComment(aComment);
    AddComment(aComment, false);
    
    // update labels
    SetLabelText();
}

void WPCommentList::CommentRemoved(WPComment aComment)
{
    RemoveComment(aComment);

    // update labels
    SetLabelText();
}

void WPCommentList::CommentAdded(WPComment aComment)
{
    AddComment(aComment, false);

    // update labels
    SetLabelText();
}

void WPCommentList::SetLabelText()
{
    if (iUnapproved) {
        QString unapprovedString = tr("Unapproved") + " (" + QString::number(iUnapproved) + ')';
        iUnapprovedLabel->setText(unapprovedString);
    }

    if (iApproved) {
        QString approved = tr("Comments") + " (" + QString::number(iApproved) + ')';
        iApprovedLabel->setText(approved);
    }
}

void WPCommentList::DecrementUnapproved()
{
    if (--iUnapproved == 0) {
        iItems.removeAt(0);
        iLayout->removeWidget(iUnapprovedLabel);
    }
}

void WPCommentList::DecrementApproved()
{
    if (--iApproved == 0) {
        // start of list, skipping possible label + unapproved comments
        int index = iUnapproved ? 1 + iUnapproved : 0;

        iItems.removeAt(index);
        iLayout->removeWidget(iApprovedLabel);
    }
}

void WPCommentList::IncrementUnapproved()
{
    if (++iUnapproved == 1) {
        iLayout->insertWidget(0, iUnapprovedLabel);
        iItems.insert(0, NULL); // placeholder
    }
}

void WPCommentList::IncrementApproved()
{
    if (++iApproved == 1) {
        // start of list, skipping possible label + unapproved comments
        int index = iUnapproved ? 1 + iUnapproved : 0;

        iLayout->insertWidget(index, iApprovedLabel);
        iItems.insert(index, NULL);
    }
}

